//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace OpenLS.UI
{
    internal sealed class WindowResizeAdorner : Adorner
    {
        // Thickness of thumb resizer
        private const int thumbThickness = 8;

        // Stores Thumbs

        private readonly VisualCollection _visualChildren;
        private readonly Window _window;
        private Point _mouseStartPosition;
        private Point _windowStartPosition;
        private Size _windowStartSize;

        public WindowResizeAdorner(UIElement element, Window window)
            : base(element)
        {
            _window = window;
            _visualChildren = new VisualCollection(element);
            createThumb(HorizontalAlignment.Left, VerticalAlignment.Stretch, Cursors.SizeWE);
            createThumb(HorizontalAlignment.Stretch, VerticalAlignment.Top, Cursors.SizeNS);
            createThumb(HorizontalAlignment.Right, VerticalAlignment.Stretch,
                                    Cursors.SizeWE);
            createThumb(HorizontalAlignment.Stretch, VerticalAlignment.Bottom, Cursors.SizeNS);
            createThumb(HorizontalAlignment.Left, VerticalAlignment.Top,
                                     Cursors.SizeNWSE);
            createThumb(HorizontalAlignment.Right, VerticalAlignment.Top, Cursors.SizeNESW);
            createThumb(HorizontalAlignment.Left, VerticalAlignment.Bottom, Cursors.SizeNESW);
            createThumb(HorizontalAlignment.Right, VerticalAlignment.Bottom, Cursors.SizeNWSE);
        }

        /// <summary>
        /// Gets the number of visual child elements within this element.
        /// </summary>
        /// <returns>
        /// The number of visual child elements for this element.
        /// </returns>
        protected override int VisualChildrenCount
        {
            get { return _visualChildren.Count; }
        }

        /// <summary>
        /// Create a new thumb
        /// </summary>
        /// <param name="horizontalAlignment">horizontalAlignment</param>
        /// <param name="verticalAlignment">verticalAlignment</param>
        /// <param name="cursor">cursor</param>
        /// <returns></returns>
        private WindowThumb createThumb(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment,
                                        Cursor cursor)
        {
            var thumb = new WindowThumb(horizontalAlignment, verticalAlignment);
            thumb.DragStarted += thumbDragStarted;
            thumb.DragDelta += thumbDragDelta;
            thumb.Cursor = cursor;

            _visualChildren.Add(thumb);

            return thumb;
        }
        /// <summary>
        /// Called when a thumb drag start occurs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumbDragStarted(object sender, DragStartedEventArgs e)
        {
            Debug.WriteLine("_window.Width = " + _window.Width + " _window.Left" + _window.Left + " _window.Height = " + _window.Height + " _window.Top = " + _window.Top);
            _mouseStartPosition = PointToScreen(Mouse.GetPosition(_window));
            _windowStartPosition = new Point(_window.Left, _window.Top);
            _windowStartSize = new Size(_window.Width, _window.Height);
        }

        /// <summary>
        /// Called when a thumb drag delta occurs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumbDragDelta(object sender, DragDeltaEventArgs e)
        {
            var thumb = (WindowThumb) sender;
            Point position = PointToScreen(Mouse.GetPosition(_window));
            Debug.WriteLine("Mouse.GetPosition(_window) = " + Mouse.GetPosition(_window));
            Debug.WriteLine("position = " + position);
            Debug.WriteLine("_mouseStartPosition = " + _mouseStartPosition);
            double deltaX = position.X - _mouseStartPosition.X;
            double deltaY = position.Y - _mouseStartPosition.Y;
            if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
            {
                setWindowWidth(_windowStartSize.Width - deltaX);
                _window.Left = _windowStartPosition.X + deltaX;
            }
            else if (thumb.HorizontalAlignment == HorizontalAlignment.Right)
            {
                Debug.WriteLine("Before set right, _windowStartSize.Width " + _windowStartSize.Width + " deltaX = " +
                                deltaX);
                setWindowWidth(_windowStartSize.Width + deltaX);
            }

            if (thumb.VerticalAlignment == VerticalAlignment.Top)
            {
                setWindowHeight(_windowStartSize.Height - deltaY);
                _window.Top = _windowStartPosition.Y + deltaY;
            }
            else if (thumb.VerticalAlignment == VerticalAlignment.Bottom)
                setWindowHeight(_windowStartSize.Height + deltaY);
        }
        /// <summary>
        /// Set the window width
        /// </summary>
        /// <param name="width"></param>
        private void setWindowWidth(double width)
        {
            Debug.WriteLine(string.Format("Setting window width to {0}", width));
            if (width < 2 * thumbThickness)
                width = 2*thumbThickness;

            _window.Width = width;
        }
        /// <summary>
        /// Set the window height
        /// </summary>
        /// <param name="height"></param>
        private void setWindowHeight(double height)
        {
            Debug.WriteLine(string.Format("Setting window height to {0}", height));
            if (height < 2*thumbThickness)
                height = 2*thumbThickness;
            
            _window.Height = height;
        }


        /// <summary>
        /// Positions child elements and determines a size for a <see cref="T:System.Windows.FrameworkElement"/> derived class. 
        /// </summary>
        /// <returns>
        /// The actual size used.
        /// </returns>
        /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (WindowThumb thumb in _visualChildren)
            {
                thumb.Arrange(thumb.GetRect(finalSize));
            }
            return finalSize;
        }

        /// <summary>
        /// Overrides <see cref="M:System.Windows.Media.Visual.GetVisualChild(System.Int32)"/>, and returns a child at the specified index from a collection of child elements. 
        /// </summary>
        /// <returns>
        /// The requested child element. This should not return null; if the provided index is out of range, an exception is thrown.
        /// </returns>
        /// <param name="index">The zero-based index of the requested child element in the collection.</param>
        protected override Visual GetVisualChild(int index)
        {
            return _visualChildren[index];
        }

        #region Nested type: WindowThumb

        private class WindowThumb : Thumb
        {
            public WindowThumb(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
            {
                Contract.Requires(horizontalAlignment != HorizontalAlignment.Stretch ||
                                  verticalAlignment != VerticalAlignment.Stretch);
                HorizontalAlignment = horizontalAlignment;
                ;
                VerticalAlignment = verticalAlignment;
                ;
                if (HorizontalAlignment != HorizontalAlignment.Stretch)
                    Width = thumbThickness;
                if (VerticalAlignment != VerticalAlignment.Stretch)
                    Height = thumbThickness;
                var borderFactory = new FrameworkElementFactory(typeof (Border));
                borderFactory.SetValue(Border.BackgroundProperty, Brushes.Transparent);

                var template = new ControlTemplate(typeof (WindowThumb)) {VisualTree = borderFactory};

                Template = template;
            }


            public Rect GetRect(Size availableSize)
            {
                double x1, x2;
                double y1, y2;
                switch (HorizontalAlignment)
                {
                    case HorizontalAlignment.Left:
                        x1 = 0;
                        x2 = thumbThickness;
                        break;
                    case HorizontalAlignment.Right:
                        x1 = availableSize.Width - thumbThickness;
                        x2 = availableSize.Width;
                        break;
                    case HorizontalAlignment.Stretch:
                        x1 = 0;
                        x2 = availableSize.Width;
                        break;
                    default:
                        throw new NotSupportedException();
                }
                switch (VerticalAlignment)
                {
                    case VerticalAlignment.Top:
                        y1 = 0;
                        y2 = thumbThickness;
                        break;
                    case VerticalAlignment.Bottom:
                        y1 = availableSize.Height - thumbThickness;
                        y2 = availableSize.Height;
                        break;
                    case VerticalAlignment.Stretch:
                        y1 = 0;
                        y2 = availableSize.Height;
                        break;
                    default:
                        throw new NotSupportedException();
                }
                return new Rect(x1, y1, x2 - x1, y2 - y1);
            }
        }

        #endregion
    }
}